package com.tianji.learning.service.impl;

import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BooleanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.SignRecord;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mapper.SignRecordMapper;
import com.tianji.learning.mq.message.SignInMessage;
import com.tianji.learning.service.ISignRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import static com.tianji.learning.constant.RedisConstants.SIGN_RECORD_KEY_PREFIX;

/**
 * <p>
 * 签到记录表 服务实现类
 * </p>
 *
 * @author XinxuanZhuo
 * @since 2024-03-07
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl extends ServiceImpl<SignRecordMapper, SignRecord> implements ISignRecordService {


    private final StringRedisTemplate redisTemplate;

    private final RabbitMqHelper mqHelper;

    /**
     * 用户签到
     */
    @Override
    public SignResultVO addSignRecords() {
        // 获取当前登录用户
        Long userId = UserContext.getUser();
        // 获取当前日期
        LocalDate now = LocalDate.now();
        // 拼接key 【前缀：userId：年月】
        String key = SIGN_RECORD_KEY_PREFIX
                + userId
                // 几年几月
                + now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
        // 计算offset
        int offset = now.getDayOfMonth() - 1;
        // 保存签到信息
        Boolean exists = redisTemplate.opsForValue().setBit(key, offset, true);
        if (BooleanUtils.isTrue(exists)) {
            throw new BizIllegalException("不允许重复签到");
        }
        // 计算连续签到天数，并返回得分
        Map<String, Integer> signDaysAndRewards = countSignDays(key, now.getDayOfMonth());

        // 发送mq消息，保存积分明细记录
        mqHelper.send(
                MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.SIGN_IN,
                SignInMessage.of(userId, 1 + signDaysAndRewards.get("rewardsPoint"))
        );

        // 封装返回
        SignResultVO vo = new SignResultVO();
        vo.setSignDays(signDaysAndRewards.get("signDays"));
        vo.setSignDays(1 + signDaysAndRewards.get("rewardsPoint"));
        return vo;
    }

    /**
     * 计算连续签到天数，并返回得分
     */
    private Map<String, Integer> countSignDays(String key, int len) {
        // 获得从这个月第一天开始的签到表
        List<Long> bitMapsRecords = redisTemplate.opsForValue().bitField(
                key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(len)).valueAt(0)
        );
        if (CollUtils.isEmpty(bitMapsRecords)) {
            Map<String, Integer> map = new HashMap<>();
            map.put("signDays", 0);
            map.put("rewardsPoint", 0);
            return map;
        }
        int num = bitMapsRecords.get(0).intValue();
        // 连续签到计数器
        int count = 0;
        // 循环，与1做与运算，得到最后一个bit，判断是否为0，为0则终止，为1则继续
        while ((num & 1) == 1) {
            // 计数器+1
            count++;
            // 把数字右移一位，最后一位被舍弃，倒数第二位成了最后一位
            num >>>= 1;
        }
        int rewardPoints = 0;
        switch (count) {
            case 7:
                rewardPoints = 10;
                break;
            case 14:
                rewardPoints = 20;
                break;
            case 28:
                rewardPoints = 40;
                break;
        }
        Map<String, Integer> map = new HashMap<>();
        map.put("signDays", count);
        map.put("rewardsPoint", rewardPoints);
        return map;
    }

    /**
     * 查询签到记录
     */
    @Override
    public List<Integer> querySignRecords() {
        // 获取当前用户id
        Long userId = UserContext.getUser();
        // 获取当前月份
        LocalDateTime now = LocalDateTime.now();
        String yyyyMM = now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
        // 拼接key sign:uid:userId:yyyyMM
        String key = SIGN_RECORD_KEY_PREFIX + userId + yyyyMM;
        // 查询redis
        int len = now.getDayOfMonth();
        // 获得从这个月第一天开始的签到表
        List<Long> bitMapsRecords = redisTemplate.opsForValue().bitField(
                key,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(len)).valueAt(0)
        );
        // 为空直接返回
        if (CollUtils.isEmpty(bitMapsRecords)) {
            return CollUtils.emptyList();
        }
        // 获取二进制数据
        int record = bitMapsRecords.get(0).intValue();

        // 封装返回
        LinkedList<Integer> resultList = new LinkedList<>();
        for (int i = 0; i < len; i++) {
            if ((record & 1) == 1) {
                resultList.addFirst(1);
            } else {
                resultList.addFirst(0);
            }
            record >>>= 1;
        }
        return resultList;
    }

}
